// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_H_
#define UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_H_

#include <memory>
#include <vector>

#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
#include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
#include "ui/gfx/geometry/rect.h"

namespace ui {
class BitmapCursorOzone;
class DrmWindowHostManager;

// DrmCursor manages all cursor state but is dependent on an injected
// proxy for how it communicates state changes to other threads or
// processes. The proxy implementation must satisfy DrmCursorProxy.
class DrmCursorProxy {
public:
    virtual ~DrmCursorProxy() { }

    // Sets the cursor |bitmaps| on |window| at |point| with |frame_delay_ms|.
    virtual void CursorSet(gfx::AcceleratedWidget window,
        const std::vector<SkBitmap>& bitmaps,
        const gfx::Point& point,
        int frame_delay_ms)
        = 0;
    // Moves the cursor in |window| to |point|
    virtual void Move(gfx::AcceleratedWidget window, const gfx::Point& point) = 0;
};

// DrmCursor manages all cursor state and semantics.
class DrmCursor : public CursorDelegateEvdev {
public:
    explicit DrmCursor(DrmWindowHostManager* window_manager);
    ~DrmCursor() override;

    // Sets or resets the DrmProxy |proxy|. If |proxy| is set, the DrmCursor uses
    // it to communicate to the GPU process or thread.
    void SetDrmCursorProxy(DrmCursorProxy* proxy);
    void ResetDrmCursorProxy();

    // Change the cursor over the specifed window.
    void SetCursor(gfx::AcceleratedWidget window, PlatformCursor platform_cursor);

    // Handle window lifecycle.
    void OnWindowAdded(gfx::AcceleratedWidget window,
        const gfx::Rect& bounds_in_screen,
        const gfx::Rect& cursor_confined_bounds);
    void OnWindowRemoved(gfx::AcceleratedWidget window);

    // Handle window bounds changes.
    void CommitBoundsChange(gfx::AcceleratedWidget window,
        const gfx::Rect& new_display_bounds_in_screen,
        const gfx::Rect& new_confined_bounds);

    // CursorDelegateEvdev:
    void MoveCursorTo(gfx::AcceleratedWidget window,
        const gfx::PointF& location) override;
    void MoveCursorTo(const gfx::PointF& screen_location) override;
    void MoveCursor(const gfx::Vector2dF& delta) override;
    bool IsCursorVisible() override;
    gfx::PointF GetLocation() override;
    gfx::Rect GetCursorConfinedBounds() override;

private:
    void SetCursorLocationLocked(const gfx::PointF& location);
    void SendCursorShowLocked();
    void SendCursorHideLocked();
    void SendCursorMoveLocked();

    // Lock-testing helpers.
    void CursorSetLockTested(gfx::AcceleratedWidget window,
        const std::vector<SkBitmap>& bitmaps,
        const gfx::Point& point,
        int frame_delay_ms);
    void MoveLockTested(gfx::AcceleratedWidget window, const gfx::Point& point);

    // The mutex synchronizing this object.
    base::Lock lock_;

    // Enforce our threading constraints.
    base::ThreadChecker thread_checker_;

    // The location of the bitmap (the cursor location is the hotspot location).
    gfx::Point GetBitmapLocationLocked();

    // The current cursor bitmap (immutable).
    scoped_refptr<BitmapCursorOzone> bitmap_;

    // The window under the cursor.
    gfx::AcceleratedWidget window_;

    // The location of the cursor within the window.
    gfx::PointF location_;

    // The bounds of the display under the cursor.
    gfx::Rect display_bounds_in_screen_;

    // The bounds that the cursor is confined to in |window|.
    gfx::Rect confined_bounds_;

    DrmWindowHostManager* window_manager_; // Not owned.

    std::unique_ptr<DrmCursorProxy> proxy_;

    DISALLOW_COPY_AND_ASSIGN(DrmCursor);
};

} // namespace ui

#endif // UI_OZONE_PLATFORM_DRM_HOST_DRM_CURSOR_H_
